ЛЕКЦИЯ 5
Символы. Строки. Множества.
Символы и строки
Перейдем
к изучению массивов специального вида - линейных массивов, состоящих только из символов,
- строк. Сами символы мы тоже не обойдем вниманием.
Описание
строк
В
разделе var строки описываются следующим образом:
var <имя_строки>: string[[<длина>]]
Максимальная
длина строки - 255 символов. Нумеруются ее компоненты начиная с 0, но этот
нулевой байт хранит длину строки.
Если
<длина> не указана, то считается, что в строке 255 символов. Поэтому для
экономии памяти следует по возможности точно указывать длину используемых
строк.
Примеры
описаний:
var s1: string[10]; (*строка
длиной 10 символов*)
s2: string; (*строка длиной 255 символов*)
Необходимо
отметить, что один символ и строка длиной в один символ
var c: char;
s: string[1];
совершенно
не эквивалентны друг другу. Вне зависимости от своей реальной длины, строка
относится к конструируемым структурированным типам данных, а не к базовым порядковым.
Символ-константа
и строка-константа
Неименованные
константы
В
тексте программы на языке Pascal последовательность
любых символов, заключенная в апострофы, воспринимается как символ или строка.
Например:
c:='z'; {c: char}
s:='abc'; {s: string}
Константе
автоматически присваивается "минимальный" тип данных, достаточный для
ее представления: char или string[k].
Поэтому попытка написать
c:='zzz';
{c: char}
вызовет
ошибку уже на этапе компиляции.
Кроме
того, не забывайте, что если константа длиннее той переменной-строки, куда ваша
программа пытается ее записать, то в момент присваивания произойдет усечение ее
до нужной длины.
Пустая
строка задается двумя последовательными апострофами:
st:= '';
Если
же необходимо сделать так, чтобы среди символов строки содержался и сам
апостроф, его нужно удвоить:
s:='Don''t worry about the
apostrophe!';
Если
теперь вывести на экран эту строку, то получится следующее:
Don't worry about the
apostrophe!
Нетипизированные константы
Все
правила задания символов и строк как неименованных констант остаются в силе и
при задании именованных нетипизированных констант в
специальном разделе const. Например:
const c3
= ''''; {это один символ -
апостроф!}
s3 = 'This is a string';
Типизированные
константы
Типизированная
константа, которая будет иметь тип char или string, задается в разделе const
следующим образом:
const c4:
char = ''''; {это
один символ - апостроф!}
s4: string[20] = 'This is a string';
Действия с символами
Операции
Результатом
унарной операции
#<положительная_неименованная_константа_целого_типа>
является
символ, номер которого в таблице ASCII соответствует заданному числу. Например,
#100 = 'd'
#39 = '''' {апостроф}
#232 = 'ш'
#1000
= 'ш' {потому что (1000 mod 256)= 232}
Кроме
того, к символьным переменным, как и к значениям всех порядковых типов данных,
применимы операции сравнения <, <>, >, =, результат которых также
опирается на номера символов из таблицы ASCII.
Стандартные
функции
Функция
chr(k:byte):char "превращает"; номер символа в символ.
Действие этой функции аналогично действию операции #. Например:
c:=
chr(48); {c: char}
{c
= '0'}
Обратной
к функции chr() является уже изученная нами функция ord(). Таким образом, для любого числа k и для любого
символа с
ord(chr(k)) = k и chr(ord(c)) = c
Стандартные
процедуры и функции pred(), succ(),
inc() и dec(), определенные
для значений любого порядкового типа, применимы также и к символам (значениям
порядкового типа данных char). Например:
pred('[') = 'Z'
succ('z') = '{'
inc('a') = 'b'
inc('c',2) = 'e'
dec('z') = 'y'
dec(#0,4) = '№' {#252}
Стандартная
функция upcase(c: char):char превращает строчную букву в прописную. Символы, не являющиеся строчными латинскими буквами, остаются без
изменения (к сожалению, в их число попадают и все русские буквы).
program code;
{Программа читает символ с клавиатуры и выводит на
экран этот
символ и соответствующий ему внутренний код}
var ch : char;
begin
write(‘Введите символ’);
readln(ch);
writeln(ch, ‘=’, ord(ch));
end.
Стандартные
функции и процедуры обработки строк
Для
обработки символьных массивов, которыми являются строки, в языке Pascal существуют специальные подпрограммы:
Функция
concat(s1,_,sN:string):string
осуществляет слияние (конкатенацию) всех перечисленных строк или символов в
указанном порядке. Если длина итоговой строки больше 255-ти символов, то
произойдет отсечение "хвоста". Кроме того, даже если результат
конкатенации не был усечен, но программа пытается сохранить его в переменную
заведомо меньшей длины, то усечение все равно состоится:
concat('abc','3de','
','X','yz') = 'abc3de Xyz'
Функция
copy(s:string;i,k:byte):string вычленяет из строки s подстроку длиной k символов,
начиная с i-го. Если i больше длины строки, то результатом будет пустая строка.
Если же k больше, чем длина оставшейся части строки, то результатом будет
только ее "хвост":
copy('abc3de Xyz',2,4) = 'bc3d'
copy('abc3de Xyz',12,4) = ''
copy('abc3de Xyz',8,14) = 'Xyz'
Процедура
delete(s:string;i,k:byte) удаляет из строки s подстроку длиной k символов, начиная с i-го. Если i больше
длины строки, то ничего удалено не будет. Если же k больше, чем длина
оставшейся части строки, то удален будет только ее "хвост":
{s = 'abc3de Xyz'} {s = 'abc3de Xyz'}
delete(s,2,3); delete(s,8,13);
{s = 'ade Xyz'} {s = 'abc3de
'}
Процедура
insert(ss,s:string;i:byte)
вставляет подстроку ss в строку s, начиная с i-го
символа. Если i выходит за конец строки, то подстрока ss
припишется в конец строки s (если результат длиннее, чем допускается для строки
s, произойдет его усечение):
{s =
'abc3de Xyz'} {s
= 'abc3de'}
insert('xyz',s,2);
insert('xyz',s,12);
{s = 'axyzbc3de Xyz'} {s =
'abc3dexyz'}
Функция
length(s:string):byte возвращает длину строки s:
length('abc3de Xyz') = 10
Функция
pos(ss,s:string):byte определяет позицию, с которой начинается первое
(считая слева направо) вхождение подстроки ss в
строку s. Если ss не встречается в s ни разу, функция
вернет 0:
pos('abc3de Xyz','X') = 8
Процедура
str(x[:w[:d]],s:string)
превращает десятичное число x (можно указать, что в этом числе w цифр, из них d
дробных) в строку s. Если число короче указанных величин, то спереди и/или
сзади оно будет дополнено пробелами:
str(156.4:7:2,s);
{s = ' 156.4 '}
Процедура
val(s:string;i:<арифметический_тип>;err:byte)
превращает строку s в десятичное число x (в случае ошибки в переменную err будет записан номер первого недопустимого символа):
{s = '15.47'}
val(s,x,err);
{x =
15.47}
Операции со строками
Сравнения
Строки
- это единственный структурированный тип данных, для элементов которого
определен порядок и, следовательно, возможны операции сравнения (=, >,
<).
На
строках определен так называемый лексикографический порядок: из двух строк
меньшей считается та, у которой первый различный символ меньше. Считается, что
пустая строка меньше любой другой строки.
Таким
образом, если начальные символы двух сравниваемых строк совпадают, то эта
совпадающая часть никак не повлияет на отношение порядка между строками,
поэтому ее можно откинуть и сравнивать только первые символы оставшихся
подстрок. Если одна из строк полностью совпадает с началом другой, то после
удаления совпадающих частей она превратится в пустую строку. Это с очевидностью
будет свидетельствовать о том, что начало слова всегда меньше, чем все слово.
Итак,
'abc'
< 'xyz'
'a' < 'abc'
'1200' < '45'
'Anny' < 'anny'
Обращение к компонентам строки
Строка
– это одномерный массив символов. Доступ к символу строки осуществляется как к элементу (компоненте) одномерного
массива.
<имя_строки>[<индекс>]
Например:
{s
= '15.47'}
c:=
s[3];
{c
= '.'}
Однако,
в отличие от массива, нельзя напрямую заменять символы в строке, то есть
действие
s[i]:=
'a';
не
вызовет ошибки при компиляции, но, скорее всего, не станет работать во время
выполнения программы. Для того чтобы изменить символ в строке, нужно
воспользоваться стандартными функциями length(), concat() и copy(). В этом случае
простое, казалось бы, действие приходится представлять как последовательность
четырех операций:
В
качестве первой подстроки взять из строки s символы с 1-го по (k-1)-й:
s1:=
copy(s,1,k-1);
В
качестве второй подстроки взять новое значение заменяемого символа:
s2:=
new_char;
В
качестве третьей подстроки взять оставшуюся часть строки s:
s3:= copy(s,k+1,length(s)-k);
Слить
эти строки воедино, а результат записать вместо исходной строки s:
s:= concat(s1,s2,s3);
Или
можно объединить все четыре действия в одном операторе:
s:= concat(copy(s,1,k-1),
new_char, copy(s,k+1,length(s)-k));
Конкатенация
Единственная
операция, которую разрешается производить с переменными строкового типа, - это
слияние строк или символов (конкатенация). Она полностью эквивалентна функции concat() и записывается при помощи знака "+".
Таким образом, предыдущий оператор можно сделать более простым:
s:=
copy(s,1,k-1) + new_char + copy(s,k+1,length(s)-k);
Пример.
Программа подсчитывает количество сочетаний “ая” в
строковой переменной.
program ST;
var t :string;
i,k:integer;
begin
k:=0;
t:=’Красная прекрасная ужасная’;
for i:=1 to
length (t)-1 do
if copy(t,i,2)=’aя’
then
k:=k+1;
writeln (k);
end.
Материал этого раздела является отчасти
"факультативным", он может выходить за пределы, предусмотренные
"программой-минимум" изучения основ программирования, однако, для
тех, кто собирается программировать всерьез, знакомство будет весьма полезным.
Множества представляют собой заранее заданные
программистом наборы возможных значений переменной. Применение множеств
позволяет удобно обращаться с данными, имеющими фиксированный набор возможных
значений, а также облегчает проверку того, попадают ли значения в нужный
диапазон.
Для объявления множества достаточно записать оператор
set of тип;
где тип -- один из определенных программистом или
предустановленных типов данных:
type charset: set of char;
var symbols: charset;
Вопреки этому примеру, стандартные типы данных мало
применимы для множеств -- ведь исходный тип набора должен быть порядковым и не
иметь более чем 256 различных значений с нижним и верхним пределом от 0 до 255
соответственно. Это связано с тем, что для хранения количества элементов
множества выделяется только один байт оперативной памяти. Приведем другой
пример для множества:
type charset= set of char;
var symbols:charset;
c1:char;
begin
symbols:=['A'..'Z','a'..'z'];
write ('Put one
symbol:');
readln (c1);
if c1 in
symbols then writeln ('OK')
else writeln ('Error');
end.
Из примера видно, что переменным типа множества можно
присваивать список диапазонов
соответствующего типа. В дальнейшем переменная типа множества может быть
использована для контроля правильности входных данных с помощью оператора in:
if c1 in symbols then writeln
('OK')
Слева от оператора in может быть указано выражение
любого перечислимого типа T, а справа -- набор с типом, совместимым с типом T.
Любые совместимые по типам данных множества можно
объединять операций "+", вычитать операцией "-" и
пересекать операцией "*". При этом результаты операций с
множествами соответствуют правилам
логики множеств:
· порядковое значение c находится в множестве A+B только
в том случае, если c находится в A или B;
· порядковое значение c находится в множестве A-B только
в том случае, если c находится в A и не находится в B;
· порядковое значение c находится в множестве A*B только
в том случае, если c находится и в A, и в B.
Если самое маленькое порядковое значение, являющееся
элементом результата операции с множеством обозначить A, а самое большое -- за
B, то тип результата становится равным A..B.
В следующем примере множество латинских букв
получается операцией сложения подмножеств латинских прописных и латинских
строчных букв.
type Latin = set of 'A'..'z';
const SmallLatin : Latin =
['A'..'Z'];
BigLatin : Latin = ['a'..'z'];
var LatinLetters : Latin;
c:char;
begin
LatinLetters := BigLatin + smallLatin;
repeat
write
('Введите символ или ',
'пробел
для выхода:');
reset (input);
readln (c);
if c in LatinLetters then
writeln (c,' - латинская буква');
until c=' ';
end.
Как в примере выше, полезно бывает создавать из
множеств подмножества при указании конструктора, содержащего выражения
диапазонов в квадратных скобках [ ... ]:
type Digits = set of 0..9; {Множество цифр}
Letters =
set of 'A'..'Z';
{Множество
латинских букв}
const EvenDigits : Digits =
[0, 2, 4, 6,
8];
{Подмножество
четных цифр}
Vowels :
Letters =
['A', 'E', 'I',
'O', 'U', 'y'];
{Подмножество
гласных букв}
HexDigits : set of '0'..'z' =
['0'..'9',
'A'..'F', 'a'..'f'];
{Символы
16-ричных чисел}
type shortWeekDays =
(
{Перечислимый
тип "дни недели"}
const Holidays : set of shortWeekDays
= [sb, Vs];
{Подмножество
"Выходные" дней недели}
var wd:shortWeekDays;
{Переменная
типа "Дни недели"}
i:integer;
begin
wd:=Pn;
for i:=1 to 7
do begin
if wd in
Holidays then
writeln (ord(wd), ' - Выходной
день')
else writeln (ord(wd), ' - Будний
день');
Inc(wd);
end;
end.
Тип данных shortWeekDays в
этом примере является перечисляемым типом. Перечисляемые типы определяют
упорядоченные наборы значений, перечисляя идентификаторы, которые обозначают
эти значения. Их порядок следует из последовательности, в которой они были
перечислены. Оператор перечисления имеет общий вид
type имя = (идентификатор,
идентификатор,..., идентификатор);
Возможные значения перечисления, заданные оператором
type, должны быть идентификаторами Паскаля, поэтому назвать дни недели
по-русски в последнем описании type было бы невозможно.
Идентификаторы, указанные в определении типа,
становятся константами перечисляемого типа, первая константа имеет порядковый
номер 0, вторая -- номер 1, и так далее:
type suit = (Club, Diamond, Heart, Spade);
При этом объявлении Heart
является константой типа suit. Стандартная функция ord возвращает порядковый номер перечислимой константы, в
нашем примере
ord(Club) = 0
ord(Diamond) = 1
ord(Heart) = 2
Как показано в листинге, переменным перечисляемого
типа можно присваивать константы, входящие в описание типа и увеличивать их
значения как любые порядковые числа оператором Inc(wd), но эти значения нельзя
читать или записывать "напрямую" операторами семейства read/write. В
качестве альтернативы их можно приводить к целочисленным значениям стандартной
функцией ord, при этом всегда первая константа списка
имеет значение 0 (в нашем случае -- константа Pn).
Оператор in в листинге позволяет проверить, попадает ли величина в
подмножество, созданное для элементов исходного типа множества. Таким образом,
основное назначение множеств и перечисляемых типов -- удобная для человека
запись выражений с "понятными" названиями констант вместо чисел. С
точки зрения компилятора данные типа множества и перечисления являются
целочисленными величинами.
Для ограничения диапазона исходных данных можно также
непосредственно объявить тип-диапазон:
type Hour=0..23; minute=0..59;
Здесь объявлены переменные типов "час" и
"минута", для переменных этих типов будут проверяться ограничения на
попадание в указанные при описании диапазоны значений. Если переменной
типа-диапазона присвоено недопустимое значение, программа отреагирует на это
сообщением “Constant out of range”.